#include "NodeVisitor.h"

using namespace std;

void DisambiguateVisitor::visit(ClassDeclarationNode *n) {
	current_class = n;
}
void DisambiguateVisitor::visit(ConstructorDeclarationNode *n) {
	current_method = n;
	current_field = 0x0;
}
void DisambiguateVisitor::visit(MethodDeclarationNode *n) {
	current_method = n;
	current_field = 0x0;
}

void DisambiguateVisitor::visit(FieldDeclarationNode *n) {
	current_method = 0x0;
	declared_fields.insert(n);
	current_field = n;
}
void DisambiguateVisitor::visit(LocalVariableDeclarationNode *n) {
}

void DisambiguateVisitor::visit(MethodInvocationNode *n) {
	NameNode *name = (NameNode*)(*n)["Name"];

	//This invocation relies on information about the primary expression
	if (!name) {
		n->method_identifier = ((LeafNode*)(*n)["identifier"])->lexeme;
		n->DeleteLeafs();
		return;
	}

	//Pop off the method identifier
	SP<ParseNode> method_name = *name->children.rbegin();
	name->children.pop_back();

	n->method_identifier = ((LeafNode*)method_name())->lexeme;

	name_is_method_invocation = true;
}

TypedNode* FindFieldIn(EnvironmentNode *env, string id) {
	if (!env) return 0x0;

	TypedNode dn;
	dn.identifier = id;

	set<TypedNode*, compare_decls>::iterator find_result = env->elements_in_scope[DeclarationNode::Expression].find(&dn);
	for (; env; env = env->next_environment) {
		find_result = env->elements_in_scope[DeclarationNode::Expression].find(&dn);
		if (find_result != env->elements_in_scope[DeclarationNode::Expression].end()) return *find_result;
	}
	return 0x0;
}

void DisambiguateVisitor::visit(NameNode *n) {
	n->declaration_is_static_context = current_method ? (current_method->modifier & ModifiedNode::Static) != 0x0 : false;
	EnvironmentNode *closest_environment = (current_method ? current_method : (EnvironmentNode*)current_class);

	ClassDeclarationNode *calling_class = current_class;

	if (n->children.empty()) {
		n->declaration_namespace = DeclarationNode::Type;
		n->declaration_position = 0;
		n->declaration_reference = current_class;
		return;
	}

	//Did a previous visitor give us a guess at the type?
	TypedNode *default_ref = 0x0;
	if (n->declaration_reference) {

		//It's a packaged type. So save it as a default, if we can't find something else
		if (n->declaration_namespace != DeclarationNode::Expression) {
			default_ref = n->declaration_reference;
			n->declaration_reference = 0x0;
		} else n->visited_locations.push_back(n->declaration_reference);
	}

	//The name isn't a local variable. Need to look at containing environments
	if (!n->declaration_reference) {
		//Look for the field starting at the current param set
		string field_name = ((LeafNode*)n->children.front()())->lexeme;

		//If we found a match...
		if ((n->declaration_reference = FindFieldIn(closest_environment, field_name))) {
			n->visited_locations.push_back(n->declaration_reference);

			//...and it's a field, not a parameter, we do things
			if (((ParseNode*)n->declaration_reference)->Type() == "FieldDeclaration") {

				//If the field isn't static, then it must be used in a non-static context, and it must abide by forward declaration
				bool is_not_static = !(n->declaration_reference->modifier & ModifiedNode::Static);

				//Context can't be static
				ParentNode *p = n->parent;

				bool is_left_side = n->parent->Type() == "LeftHandSide";

				for (; p != current_field && p != current_method; p = p->parent);
				error |= (((ModifiedNode*)p)->modifier & ModifiedNode::Static) != 0x0 && is_not_static;

				//if it isn't the left hand side of an assignment and we're in a field initialisation
				if (!is_left_side && p == current_field) {
					TypedNode tn;
					tn.identifier = field_name;

					//Error if we're using the field being declared or if the field being asked for is part of this class, but not declared yet
					error |= current_field->identifier == field_name || (declared_fields.find(&tn) == declared_fields.end() && current_class->elements_in_scope[DeclarationNode::Expression].find(&tn) != current_class->elements_in_scope[DeclarationNode::Expression].end());
				}

				//Can't have an implicit "this" on fields (params are never static)

				if (error) return;

			}
		}
	}

	unsigned int pos = 1;

	//The first identifier isn't a param or field. It's infact a type name
	if (!n->declaration_reference) {
		//We also have no default
		if (!default_ref) {
			error = true;
			return;
		}

		//this is a package type
		n->declaration_namespace = DeclarationNode::Type;
		n->declaration_reference = default_ref;
		pos = n->declaration_position;
		//Possible that this name is simply trying to describe a type, which is used in method invocation (EG. String.valueOf(); //The String is the context. valueOf is the method)
		if (pos == n->children.size()) {
			if (!name_is_method_invocation) error = true;
			n->declaration_is_static_context = true;
			return;
		}
		
		calling_class = default_ref->type_meta.reference;
		//There is more
		n->declaration_reference = FindFieldIn(((ClassDeclarationNode*)n->declaration_reference), ((LeafNode*)n->children[pos++]())->lexeme);

		//The next thing better be a static field that we can access
		if (!n->declaration_reference || !(((FieldDeclarationNode*)n->declaration_reference)->modifier & ModifiedNode::Static) || !CanAccessMember(n->declaration_reference, current_class, current_class)) {
			error = true;
			return;
		}

		n->declaration_namespace = DeclarationNode::Expression;
		n->visited_locations.push_back(n->declaration_reference);
	}

	n->declaration_is_static_context = false;
	//We have the base. Now the rest is just redirecting, looking for non-static fields (methods are contextual around method invocation nodes. They handle what to do if we want a method)
	for (; pos < n->children.size(); ++pos ){
		//Next id to find
		string id = ((LeafNode*)n->children[pos]())->lexeme;

		FieldDeclarationNode *current_field = (FieldDeclarationNode*)n->declaration_reference;

		calling_class = current_field->type_meta.reference;

		//Special case for if the length field being accessed in a name node
		if (current_field->type_meta.category & TypeMeta::Array) {
			error |= id != "length" || n->parent->Type() == "LeftHandSide";
			if (!error) {
				//Put this as a field access on the array
				FieldAccessNode *fan = new FieldAccessNode();
				fan->children.push_back(n);
				fan->children.push_back(new LeafNode("identifier", "length"));
				n->ReplaceMeWith(fan);
				n->children.pop_back();
				n->parent = fan;
			}
			return;
		} 
		//If this field is not a reference type, then we can't do anything more with it
		if (!((current_field->type_meta.category & TypeMeta::Reference) || (current_field->type_meta.category & TypeMeta::String))) break;

		//Find it in the current reference's type's environment
		n->declaration_reference = FindFieldIn(current_field->type_meta.reference, id);

		//We found something, it's not static and we can access it => we can continue to the next field
		if (n->declaration_reference && !(n->declaration_reference->modifier & ModifiedNode::Static) && CanAccessMember(n->declaration_reference, current_class, calling_class)) {
			n->visited_locations.push_back(n->declaration_reference);
			continue;
		}

		//Boo, man. You didn't find anything or it was static on a non-static or you couldn't access it
		error = true;
		return;
	}

	//We resolved literally as much as we can. If there is anything left over, there is a big issue
	error |= pos < n->children.size();
	if (error) return;

	n->declaration_namespace = DeclarationNode::Expression;
	name_is_method_invocation = false;
}